/**
 * @description: require.js
 * @date: 2020/12/23
 */

(function f(global) {
    let register = {}; // 保存加载的模块，但还没初始化的
    let cacheModules = {}; // 管理所有加载好的模块
    let baseUrlRoot = 'modules/';

    // 内定模块
    let handles = {
        require: function () {
            return require;
        },
        module: function (mod) {
            if (!mod.module) {
                mod.module = {
                    id: mod.id,
                    exports: mod.exports || (mod.exports = {})
                }
            }
            return mod.module
        },
        exports: function (mod) {
            if (!mod.exports) {
                mod.exports = {}
            }
            return mod.exports;
        }
    }

    function isFunction(fn) {
        return typeof fn === 'function';
    }

    let nameIndex = 0;
    function getName() {
        let name = document.currentScript && document.currentScript.src || '';
        return name.split(baseUrlRoot)[1] ? baseUrlRoot + name.split(baseUrlRoot)[1] : `_@module_${++nameIndex}`;
    }

    let eventMap = {};
    function on(name, callback) {
        let module = cacheModules[name];

        if (module) {
            callback(module)
        } else {
            let cbs = eventMap[name];
            if (!cbs) {
                cbs = eventMap[name] = [];
            }

            cbs.push(callback);
        }
    }
    function emit(name, params) {
        let cbs = eventMap[name];
        cbs && cbs.forEach(item => item(params));
        eventMap[name] = [];
    }

    // 执行模块代码
    function execCb(module) {

        if (module.isInit) {
            return;
        }

        module.isInit = true;
        let args = module.depends.map(depId => {

            if (handles[depId]) {
                return handles[depId](module);
            }

            return cacheModules[depId] && cacheModules[depId].exports;
        });

        // 模块可以不定义为函数
        let exports = isFunction(module.factory)? module.factory.apply(window, args) : module.factory;
        exports && (module.exports = exports);
    }

    // 检测当前模块的依赖是否加载完成， 加载完成则执行模块代码
    function check(module) {
        let depsIsAllReady = module.depends.every(depId => module.depsMap[depId]);

        if (depsIsAllReady && !cacheModules[module.id]) {
            execCb(module);
            cacheModules[module.id] = module;
            delete register[module.id];
            emit(module.id, module);
        }
    }

    // 检查是否有加载完成但没执行的模块
    function checkLoaded() {
        let cycleCheckModule = [];

        Object.keys(register).forEach(modId => {
            if (!register[modId].isDefine) {
                cycleCheckModule.push(register[modId]);
            }
        })

        function breakCycle(module, traced = {}, processed = {}) {
            traced[module.id] = true;

            module.depends.forEach(depId => {
                let depMod = register[depId]; // 此时还不在cacheModules中

                if (depMod && !processed[depId] && !module.depsMap[depId]) {
                    if (traced[depId]) { // 循环引用了
                        module.depsMap[depId] = true;
                        check(module);
                    } else {
                        breakCycle(depMod, traced, processed);
                    }
                }

            })

            // 已经处理过的，则表示已经解决引用问题，模块也初始化完成
            // eg: a.js->[b.js], b.js->[a.js], c.js->[a.js], main.js->[a.js, c.js]
            processed[module.id] = true;
        }

        cycleCheckModule.forEach(item => breakCycle(item));
    }

    // 脚本加载完后的处理
    function onScriptLoad(evt) {
        let id = evt.target.getAttribute('data-id');
        let module = register[id];

        module.isLoad = true;

        // 如果有依赖，递归加载
        if (Array.isArray(module.depends) && module.depends.length) {
            loadModule(module)
        }

        checkLoaded()
    }

    function loadModule(mod) {
        let deps = mod.depends;

        deps.forEach(id => {
            if (handles[id]) {
                mod.depsMap[id] = true;
                return;
            }

            if (!cacheModules[id] && !register[id]) { // 模块还没加载过，则加载
                addOutsideResource('js', id, [{key: 'data-id', value: id}]).then(onScriptLoad)
            }

            // 监听加载完成的事件处理
            on(id, function () {
                mod.depsMap[id] = true;
                check(mod);
            });
        })

        check(mod);
    }

    // 创建模块对象
    function createModule(name, deps, factory) {
        return {
            id: name || getName(), // require定义的模块的没有name
            isDefine: !!name, // 是否是define定义的模块
            depends: deps || [],
            exports: void 0,
            factory: factory,
            isLoad: false, // 加载完成
            isInit: false,
            depsMap: {} // 记录依赖是否完成
        }
    }

    // 模块加载器
    function require(depends, factory) {

        if (typeof depends === "string") { // 表示内部引用
            let exports = cacheModules[depends] && cacheModules[depends].exports;

            if (!cacheModules[depends]) {
                console.error(`module "${depends}" is no defined`);
            }

            return exports;
        }

        let module = createModule(null, depends, factory);

        register[module.id] = module;

        // 加载依赖然后执行
        loadModule(module);
    }

    // 定义模块
    function define(name, depends, factory) {
        if (typeof name !== 'string') {
            factory = depends;
            depends = name;
            name = getName();
        }

        if (isFunction(depends)) {
            factory = depends;
            depends = null;
        }

        if (!Array.isArray(depends) && isFunction(factory)) {
            depends = ['require', 'exports', 'module'];
        }

        register[name] = createModule(name, depends, factory);
    }

    !global.require && (global.require = require);
    !global.define && (global.define = define);

    define.amd = {} // 标识是amd模块加载器
})(window)
